package com.common.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.common.annotation.Table;
import com.common.dao.BaseDao;
import com.common.entity.SupperEntity;
import com.common.exception.ErrorException;
import com.common.model.SupperModel;
import com.common.mysql.KeyAndValuePair;
import com.common.mysql.SearchPageResult;
import com.common.mysql.Where;
import com.common.mysql.convert.AbstractConvert;
import com.common.mysql.enums.OperatorTypeEnum;
import com.common.mysql.enums.SqlOperatorEnum;
import com.common.repository.BaseRepository;
import com.common.service.BaseService;
import com.common.utils.DeepCopyUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class BaseServiceImpl<I, M extends SupperModel<I>, E extends SupperEntity<I>, R extends BaseRepository<I, E, D>, D extends BaseDao<I, E>> implements BaseService<I, M, E> {

    @Autowired
    protected R repository;

    @Override
    public List<M> searchAll() {
        String tableName = getTableName();
        List<KeyAndValuePair> fields = getEntityFields();
        getOrderBy(fields);
        List<E> entityList = repository.searchAll(tableName, fields);
        List<M> modelList = new LinkedList<>();
        convertEntityListToModelList(entityList, modelList);
        return modelList;
    }

    @Override
    public M getById(I id) {
        List<KeyAndValuePair> fields = getEntityFields();
        String tableName = getTableName();
        E entity = repository.getById(id, tableName, fields);
        return convertEntityToModel(entity);
    }

    @Override
    public List<M> search(Where where) {
        String tableName = getTableName();
        List<KeyAndValuePair> fields = getEntityFields();
        getOrderBy(fields);
        List<E> entityList = repository.search(tableName, fields, where);
        List<M> modelList = new LinkedList<>();
        convertEntityListToModelList(entityList, modelList);
        return modelList;
    }

    @Override
    public int update(M model) {
        String tableName = getTableName();
        E entity = DeepCopyUtils.deepCopy(model, getEntityClass());
        List<KeyAndValuePair> fields = setEntityFields(entity, false);
        int count = repository.update(model.getId(), tableName, fields);
        if (count == 0) {
            throw new ErrorException("更新失败！");
        }
        return count;
    }


    private E getEntityInstance() {
        try {
            return getEntityClass().newInstance();
        } catch (Exception e) {
            throw new ErrorException(e);
        }
    }

    /**
     * 将entity值转为数据库可识别的
     *
     * @param entity
     * @param insertFlag
     * @return
     */
    private List<KeyAndValuePair> setEntityFields(E entity, boolean insertFlag) {
        List<KeyAndValuePair> keyAndValuePairList = new LinkedList<>();
        Object id = null;
        //循环获取该类及其父类所有的属性
        for (Class entityClass = getEntityClass(); !Object.class.equals(entityClass); entityClass = entityClass.getSuperclass()) {
            Field[] entityFields = entityClass.getDeclaredFields();
            for (Field field : entityFields) {
                com.common.annotation.Field fieldAnnotation = field.getAnnotation(com.common.annotation.Field.class);
                //数据库字段名
                field.setAccessible(true);
                String fieldName = null;
                AbstractConvert convertObject = null;
                if (!ObjectUtils.isEmpty(fieldAnnotation)){
                    //是否有忽略的字段 ，有则跳过
                    boolean ignoreFlag = fieldAnnotation.ignoreFlag();
                    if (ignoreFlag){
                        continue;
                    }
                    if (StringUtils.hasText(fieldAnnotation.alias())) {
                        fieldName = fieldAnnotation.alias();
                        Class convert = fieldAnnotation.convert();
                        if (!AbstractConvert.class.equals(convert)){
                            try {
                                convertObject = (AbstractConvert) convert.newInstance();
                            } catch (Exception e) {
                                throw new ErrorException(e);
                            }
                        }
                    }
                } else {
                    fieldName = field.getName();
                }
                try {
                    //如果不存在值则不会对相应字段进行更新
                    Object value = field.get(entity);
                    //判断新增时是否已经填写了主键
                    if ("id".equals(fieldName)){
                        if (ObjectUtils.isEmpty(value)){
                            continue;
                        }
                        id = value;
                    }
                    if (null != value) {
                        if (!ObjectUtils.isEmpty(convertObject)){
                            value = convertObject.convert(value);
                        }
                        //字段名 值
                        keyAndValuePairList.add(KeyAndValuePair.build(OperatorTypeEnum.FILED_VALUE.ordinal(), fieldName, value));
                    }else if (!ObjectUtils.isEmpty(fieldAnnotation) && fieldAnnotation.valueNullUpdate()){
                        //字段名 值
                        keyAndValuePairList.add(KeyAndValuePair.build(OperatorTypeEnum.FILED_VALUE.ordinal(), fieldName, null));
                    }
                } catch (Exception e) {
                    throw new ErrorException(e);
                }

            }
        }
        //根据table注解来自动配置生成主键的方式
        if (insertFlag && ObjectUtils.isEmpty(id)) {
            Class<E> entityClass = getEntityClass();
            Table tableAnnotation = entityClass.getAnnotation(Table.class);
            boolean generatorFlag = tableAnnotation.generatorFlag();
            if (generatorFlag) {
                try {
                    Object generator = tableAnnotation.idGenerator().newInstance().generator();
                    keyAndValuePairList.add(KeyAndValuePair.build(OperatorTypeEnum.FILED_VALUE.ordinal(), "id", generator));
                } catch (Exception e) {
                    throw new ErrorException(e);
                }
            } else {
                throw new ErrorException("id生成器未启用！");
            }
        }
        return keyAndValuePairList;
    }

    @Override
    public int add(M model) {
        String tableName = getTableName();
        E entity = DeepCopyUtils.deepCopy(model, getEntityClass());
        List<KeyAndValuePair> keyAndValuePairList = setEntityFields(entity, true);
        int count = repository.add(tableName, keyAndValuePairList);
        //新增成功，查询当前信息并返回
        if (count == 0) {
            throw new ErrorException("新增失败！");
        }
        return count;
    }

    @Override
    public int delete(I id) {
        String tableName = getTableName();
        boolean delFlag = getDelFlag();
        if (delFlag) {
            List<KeyAndValuePair> fields = new LinkedList<>();
            fields.add(KeyAndValuePair.build(OperatorTypeEnum.FILED_VALUE.ordinal(), "del_flag", "1"));
            return repository.update(id, tableName, fields);
        }
        return repository.delete(id, tableName);
    }

    @Override
    public M searchOne(Where where) {
        List<KeyAndValuePair> fields = getEntityFields();
        String tableName = getTableName();
        getOrderBy(fields);
        E entity = repository.searchOne(tableName, fields, where);
        return convertEntityToModel(entity);
    }

    /**
     * 获取排序字段
     * @param fields
     */
    private void getOrderBy(List<KeyAndValuePair> fields) {
        Class<E> entityClass = getEntityClass();
        Table tableAnnotation = entityClass.getAnnotation(Table.class);
        String orderBy = tableAnnotation.orderBy();
        if (!StringUtils.hasText(orderBy)){
            return;
        }
        fields.add(KeyAndValuePair.build(OperatorTypeEnum.ORDER_BY_TYPE.ordinal(), SqlOperatorEnum.ORDER_BY.toString(), orderBy));
    }

    @Override
    public SearchPageResult<M> searchPage(int page, int limit) {
        SearchPageResult<M> result = new SearchPageResult<>();
        result.setPage(page);
        result.setLimit(limit);
        List<M> modelList = new LinkedList<>();
        String tableName = getTableName();
        List<KeyAndValuePair> fields = getEntityFields();
        Where where = Where.build().limit(page, limit);
        List<E> entityList = repository.search(tableName, fields, where);
        convertEntityListToModelList(entityList, modelList);
        result.setList(modelList);
        int totalCount = count(where);
        result.setTotalCount(totalCount);
        return result;
    }

    @Override
    public int count(Where where) {
        String tableName = getTableName();
        return repository.count(tableName,where);
    }

    /**
     * 将entityList集合转为modelList
     *
     * @param entityList
     * @param modelList
     */
    private void convertEntityListToModelList(List<E> entityList, List<M> modelList) {
        for (int i = 0; i < entityList.size(); i++) {
            M model = convertEntityToModel(entityList.get(i));
            if (ObjectUtils.isEmpty(model)) {
                continue;
            }
            modelList.add(model);
        }
    }

    /**
     * 单个对象的转换
     *
     * @param entity
     * @return
     */
    private M convertEntityToModel(E entity) {
        if (ObjectUtils.isEmpty(entity)) {
            return null;
        }
        M model = getModelInstance();
        BeanUtils.copyProperties(entity, model);
        return model;
    }

    /**
     * 获取要查询的字段
     *
     * @return
     */
    private List<KeyAndValuePair> getEntityFields() {
        Class<E> entityClass = getEntityClass();
        List<KeyAndValuePair> keyAndValuePairList = new LinkedList<>();
        //循环获取该类及其父类所有的属性
        for (Class entity = entityClass; !Object.class.equals(entity); entity = entity.getSuperclass()) {
            Field[] entityFields = entity.getDeclaredFields();
            Arrays.stream(entityFields).forEach(field -> {
                com.common.annotation.Field fieldAnnotation = field.getAnnotation(com.common.annotation.Field.class);
                if (!ObjectUtils.isEmpty(fieldAnnotation) && StringUtils.hasText(fieldAnnotation.alias())) {
                    keyAndValuePairList.add(KeyAndValuePair.build(OperatorTypeEnum.FILED_VALUE.ordinal(), fieldAnnotation.alias()));
                } else {
                    keyAndValuePairList.add(KeyAndValuePair.build(OperatorTypeEnum.FILED_VALUE.ordinal(), field.getName()));
                }
            });
        }
        return keyAndValuePairList;
    }

    /**
     * 获取entity类
     *
     * @return
     */
    protected Class<E> getEntityClass() {
        Class<E> entityClass = null;
        Class<? extends BaseServiceImpl> serviceClass = getClass();
        Type type = serviceClass.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            entityClass = (Class<E>) parameterizedType.getActualTypeArguments()[2];
        }
        return entityClass;
    }

    /**
     * 获取Model类
     *
     * @return
     */
    private Class<M> getModelClass() {
        Class<M> modelClass = null;
        Class<? extends BaseServiceImpl> serviceClass = getClass();
        Type type = serviceClass.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            modelClass = (Class<M>) parameterizedType.getActualTypeArguments()[1];
        }
        return modelClass;
    }

    private M getModelInstance() {
        Class<M> modelClass = getModelClass();
        try {
            return modelClass.newInstance();
        } catch (Exception e) {
            throw new ErrorException(e.getMessage());
        }
    }

    /**
     * 获取表名
     *
     * @return
     */
    private String getTableName() {
        Class<E> entityClass = getEntityClass();
        Table tableAnnotation = entityClass.getAnnotation(Table.class);
        if (ObjectUtils.isEmpty(tableAnnotation)) {
            throw new ErrorException("未获取到表名！");
        }
        return tableAnnotation.value();
    }

    /**
     * 获取表名
     *
     * @return
     */
    private boolean getDelFlag() {
        Class<E> entityClass = getEntityClass();
        Table tableAnnotation = entityClass.getAnnotation(Table.class);
        if (ObjectUtils.isEmpty(tableAnnotation)) {
            throw new ErrorException("未获取到表名！");
        }
        return tableAnnotation.delFlag();
    }
}
